package com.baichen.eventloop;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class EventLoopImpl implements EventLoop {

    private static final int DEFAULT_QUEUE_SIZE = 1024;

    // WAKE_UP的设计是为了让事件循环在没有普通任务、但有新定时任务时能及时处理，不会因为阻塞而延迟定时任务的执行。
    private static final Runnable WEAK_UP =() -> {
        // Do nothing, just a weak up task
    };

    private static final AtomicInteger id = new AtomicInteger();

    private final BlockingQueue<Runnable> taskQueue;

    private final PriorityBlockingQueue<ScheduleTask>  scheduleTaskQueue;

    private final Thread thread;

    public EventLoopImpl() {
        taskQueue = new ArrayBlockingQueue<>(DEFAULT_QUEUE_SIZE);
        scheduleTaskQueue = new PriorityBlockingQueue<>(DEFAULT_QUEUE_SIZE);
        thread = new EventLoopThread("Baichen-EventLoop-" + id.incrementAndGet());
        thread.start();
    }

    @Override
    public void execute(Runnable task) {
        if (!taskQueue.offer(task)) {
            throw new RuntimeException("Task queue is full");
        }
    }

    @Override
    public void schedule(Runnable task, long delay, TimeUnit unit) {
        ScheduleTask scheduleTask = new ScheduleTask(task, unit.toMillis(delay) + System.currentTimeMillis(), 0, this);
        if (!scheduleTaskQueue.offer(scheduleTask)) {
            throw new RuntimeException("Schedule Task queue is full");
        }
        // 这里使用WEAK_UP任务来唤醒事件循环线程，确保定时任务能及时执行
        execute(WEAK_UP);
    }

    @Override
    public void scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
        ScheduleTask scheduleTask =
                new ScheduleTask(task, unit.toMillis(initialDelay) + System.currentTimeMillis(), unit.toMillis(period), this);
        if (!scheduleTaskQueue.offer(scheduleTask)) {
            throw new RuntimeException("Schedule Task queue is full");
        }
        // 这里使用WEAK_UP任务来唤醒事件循环线程，确保定时任务能及时执行
        execute(WEAK_UP);
    }

    @Override
    public EventLoop next() {
        return this;
    }

    @Override
    public Queue<ScheduleTask> getScheduleTaskQueue() {
        return scheduleTaskQueue;
    }

    private Runnable getTask() {
        Runnable task = scheduleTaskQueue.peek();
        if (task == null) {
            try {
                task = taskQueue.take();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (task == WEAK_UP) {
                task = null;
            }
            return task;
        }

        ScheduleTask scheduleTask = (ScheduleTask) task;
        if (scheduleTask.getDeadline() <= System.currentTimeMillis()) {
            return scheduleTask;
        }

        try {
            scheduleTask = scheduleTaskQueue.poll(scheduleTask.getDeadline() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            if (scheduleTask == WEAK_UP) {
                return null;
            }
        } catch (InterruptedException ignored) {
        }
        return scheduleTask;
    }

    class EventLoopThread extends Thread {

        public EventLoopThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                Runnable task = getTask();
                if (task != null) {
                    task.run();
                }
            }
        }
    }
}

